package ua.dlab.ui;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.io.File;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

public abstract class Step2 implements Step {
	private JPanel pnlMain = null;
	private int matrixSize = 0;
	private int eps = 0;
	private Step nextStep = null;
	
	private double matrix[][] = null;
	private boolean load = false;
	
	public Step2(int size, int eps, double[][] matrix) {
		matrixSize = size;
		this.eps = eps;
		this.matrix = matrix;
		
		if (matrix != null) {
			load = true;
		}
	}
	
	private void init() {
		pnlMain = new JPanel(new GridBagLayout());
		
		pnlMain.add(new JLabel("Заповніть матрицю:"), 
				new GridBagConstraints(0, 0, matrixSize == 0 ? 1 : matrixSize + 1, 1, 0, 0, GridBagConstraints.NORTHWEST,
						GridBagConstraints.NONE, new Insets(6, 6, 6, 6), 0, 0));
		
		if (!load) {
			matrix = new double[matrixSize][matrixSize + 1];
		}
		
		for (int i = 0; i < matrixSize; i++) {
			for (int j = 0; j < matrixSize + 1; j++) {
				JPanel pnlCell = new JPanel();
				
				if (j == 0) {
					// 
				} else if (j == matrixSize) {
					pnlCell.add(new JLabel("="));
				} else {
					pnlCell.add(new JLabel("+"));
				}
				
				pnlCell.add(new Spinner(i, j));
				
				if (j != matrixSize) {
					pnlCell.add(new JLabel(" X" + (j + 1)));
				}
				
				pnlMain.add(pnlCell, 
						new GridBagConstraints(j, i + 1, 1, 1, 0, 0, GridBagConstraints.NORTHWEST,
								GridBagConstraints.NONE, new Insets(6, 6, 6, 6), 0, 0));
			}
		}
		
		pnlMain.add(new JPanel(), 
				new GridBagConstraints(0, matrixSize + 1, matrixSize == 0 ? 1 : matrixSize + 1, 1, 1, 1, GridBagConstraints.NORTHWEST,
						GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
	}

	@Override
	public boolean hashNext() {
		return true;
	}

	@Override
	public Step nextStep() {
		if (nextStep == null) {
			nextStep = new Step3(matrix, matrixSize, eps) {
				@Override
				public Step previousStep() {
					return Step2.this;
				}
			};
		}
		
		return nextStep;
	}

	@Override
	public JComponent getMainArea() {
		if (pnlMain == null) {
			init();
		}
		
		return new JScrollPane(pnlMain);
	}

	@Override
	public boolean hashPrevious() {
		return true;
	}
	
	@Override
	public Action getAction() {
		return new AbstractAction("Зберегти дані") {
			private static final long serialVersionUID = 1L;

			@Override
			public void actionPerformed(ActionEvent e) {
				save();
			}
		};
	}
	
	private void save() {
		JFileChooser jfcSave = new JFileChooser();
		jfcSave.setFileFilter(new FileNameExtensionFilter("Xml files (*.xml)", "xml"));
		
		if (jfcSave.showSaveDialog(pnlMain) == JFileChooser.APPROVE_OPTION) {
			if (jfcSave.getSelectedFile().exists()) {
				int answer = JOptionPane.showConfirmDialog(pnlMain, 
						"Файл вже існує. Ви точно бажаєте його перезаписати?", "Перезапис файлу", JOptionPane.YES_NO_OPTION);
				if (answer == JOptionPane.YES_OPTION) {
					saveFile(jfcSave.getSelectedFile().getAbsolutePath());
				}
			} else {
				saveFile(jfcSave.getSelectedFile().getAbsolutePath());
			}
		}
	}
	
	private void saveFile(String filePath) {
		if (matrix != null) {
			DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
			
			try {
				DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();
				Document document = documentBuilder.newDocument();
				Element rootElement = document.createElement("data");
				document.appendChild(rootElement);
				
				rootElement.setAttribute("size", String.valueOf(matrixSize));
				rootElement.setAttribute("eps", String.valueOf(eps));
				for (int i = 0; i < matrixSize; i++) {
					Element row = document.createElement("row");
					rootElement.appendChild(row);
					
					for (int j = 0; j < matrixSize + 1; j++) {
						Element cell = document.createElement("cell");
						cell.setTextContent(String.valueOf(matrix[i][j]));
						
						row.appendChild(cell);
					}
				}
				
				TransformerFactory transformerFactory = TransformerFactory.newInstance();
				Transformer transformer = transformerFactory.newTransformer();
				DOMSource domSource = new DOMSource(document);
				StreamResult streamResult = new StreamResult(new File(filePath.endsWith(".xml") ? filePath : filePath + ".xml"));
				transformer.transform(domSource, streamResult);
			} catch (ParserConfigurationException | TransformerException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public abstract Step previousStep();
	
	private class Spinner extends JSpinner {
		private static final long serialVersionUID = 1L;
		private int i = -1;
		private int j = -1;

		public Spinner(int i, int j) {
			this.i = i;
			this.j = j;
			
			if (!load) {
				setModel(new SpinnerNumberModel(1, -100, 100, 0.001));
				matrix[i][j] = 1;
			} else {
				setModel(new SpinnerNumberModel(matrix[i][j], -100, 100, 0.001));
			}

			addChangeListener(new ChangeListener() {
				@Override
				public void stateChanged(ChangeEvent e) {
					matrix[Spinner.this.i][Spinner.this.j] = (double)Spinner.this.getValue();
					nextStep = null;
				}
			});
		}
	}
}
